import {
  AxiosRequestConfig,
  AxiosPromise,
  METHODS,
  AxiosResponse,
  ResolvedFn,
  RejectedFn
} from './../types/index'
import dispatchRequest from './dispatchRequest'
import InterceptorsManager from './InterceptorsManager'
import mergeConfig from './mergeConfig'

interface Interceptors {
  request: InterceptorsManager<AxiosRequestConfig>
  response: InterceptorsManager<AxiosResponse>
}
interface PromiseChain {
  resolved: ResolvedFn | ((config: AxiosRequestConfig) => AxiosPromise)
  rejected?: RejectedFn
}
export class Axios {
  defaults: AxiosRequestConfig
  interceptors: Interceptors
  constructor(config: AxiosRequestConfig) {
    this.defaults = config
    this.interceptors = {
      request: new InterceptorsManager<AxiosRequestConfig>(),
      response: new InterceptorsManager<AxiosResponse>()
    }
  }
  request(url?: any, config?: any): AxiosPromise {
    if (typeof url === 'string') {
      if (!config) {
        config = {}
      }
      config.url = url
    } else {
      config = url
    }

    config = mergeConfig(this.defaults, config)
    const chain: PromiseChain[] = [
      {
        resolved: dispatchRequest,
        rejected: undefined
      }
    ]
    // request中注册的拦截器，先注册的后运行
    this.interceptors.request.forEach(intercepter => {
      // 放置在dispatchRequest方法之前执行
      chain.unshift(intercepter)
    })
    // response中注册的方法拦截器，先注册的先执行
    this.interceptors.response.forEach(item => {
      // 放置在dispatchRequest方法后面执行
      chain.push(item)
    })
    let promise = Promise.resolve(config)
    while (chain.length) {
      let { resolved, rejected } = chain.shift()!
      promise = promise.then(resolved, rejected)
    }
    return promise
    // return dispatchRequest(config)
  }
  get(url: string, config?: AxiosRequestConfig): AxiosPromise {
    return this._requestWithoutData('get', url, config)
  }
  delete(url: string, config?: AxiosRequestConfig): AxiosPromise {
    return this._requestWithoutData('delete', url, config)
  }
  head(url: string, config?: AxiosRequestConfig): AxiosPromise {
    return this._requestWithoutData('head', url, config)
  }
  options(url: string, config?: AxiosRequestConfig): AxiosPromise {
    return this._requestWithoutData('options', url, config)
  }
  post(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise {
    return this._requsetWithData('post', url, data, config)
  }
  put(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise {
    return this._requsetWithData('put', url, data, config)
  }
  patch(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise {
    return this._requsetWithData('patch', url, data, config)
  }
  _requestWithoutData(method: METHODS, url: string, config?: AxiosRequestConfig): AxiosPromise {
    return this.request(
      Object.assign(config || {}, {
        method,
        url
      })
    )
  }
  _requsetWithData(
    method: METHODS,
    url: string,
    data?: any,
    config?: AxiosRequestConfig
  ): AxiosPromise {
    return this.request(
      Object.assign(config || {}, {
        method,
        url,
        data
      })
    )
  }
}
